package service

import (
	"errors"
	"ia/apps/admin/middleware/jwt"
	"ia/apps/admin/web/dts/sin"
	"ia/apps/admin/web/dts/sout"
	"ia/apps/admin/web/repository"
	"ia/common/icasbin"
	"ia/common/model/modeladmin"
	"ia/common/storage"
	"ia/common/support"
	"ia/common/support/define"
	"ia/common/support/tools"
	"strconv"

	"github.com/kataras/golog"
	"github.com/kataras/iris/v12"
	"gorm.io/gorm"
)

type AdminUserService interface {
	Login(iris.Context)
	ResetPassword(iris.Context)

	Create(iris.Context)
	Delete(iris.Context)
	Update(iris.Context)
	Table(iris.Context)
	List(iris.Context)
}

func NewAdminlUserService() *adminUserService {
	return &adminUserService{}
}

type adminUserService struct{}

func (impl *adminUserService) Login(ctx iris.Context) {
	var (
		code       define.Code
		err        error
		in         = new(sin.AdminUserLoginForm)
		mUser      = new(modeladmin.AdminUser)
		ckPassword bool
		token      string
	)
	// TODO 同时登陆一个账号，将已登陆的用户挤下线
	if err = ctx.ReadJSON(&in); err != nil {
		code = define.CodeParamsError
		goto ERR
	}

	if err = storage.GDB1.Take(&mUser, "username=?", in.Username).Error; err != nil {
		if errors.Is(gorm.ErrRecordNotFound, err) {
			code = define.CodeRecordNotFound
		}
		goto ERR
	}
	if mUser.Enable <= 0 {
		support.Error(ctx, iris.StatusBadRequest, define.CodeUserDisabled)
		return
	}

	ckPassword = support.CheckPassword(mUser.Password, in.Password)
	if !ckPassword {
		code = define.CodePasswordError
		goto ERR
	}

	if token, err = jwt.GenerateToken(mUser); err != nil {
		code = define.CodeFailure
		goto ERR
	}

	support.Ok(ctx, iris.Map{
		"token": token,
	})
	return
ERR:
	golog.Errorf("用户[%s]登录失败。原因:%s,错误:%v", in.Username, code.String(), err)
	support.InternalServerError(ctx, code)
}

func (impl *adminUserService) GetUserInfo(ctx iris.Context) {
	var (
		code  define.Code
		err   error
		uid   = ctx.Values().GetIntDefault(define.Uid, 0)
		mUser = new(modeladmin.AdminUser)
	)

	if err = storage.GDB1.Omit("password").
		Take(&mUser, "id=?", uid).Error; err != nil {
		if errors.Is(gorm.ErrRecordNotFound, err) {
			code = define.CodeRecordNotFound
		}
		goto ERR
	}

	support.Ok(ctx, mUser)
	return
ERR:
	golog.Errorf("操作失败。原因:%s,错误:%v", code.String(), err)
	support.InternalServerError(ctx, code)
}

func (impl *adminUserService) Logout(ctx iris.Context) {
	// TODO 清除 redis
	ctx.Values().Reset()

	support.Ok_(ctx)
	// ERR:
	// 	golog.Errorf("操作失败。错误:%v", err)
	// 	support.InternalServerError(ctx, define.CodeFailure)
}

func (impl *adminUserService) ResetPassword(ctx iris.Context) {
	var (
		code               define.Code
		err                error
		params             = new(sin.ResetPassword)
		mUser              = new(modeladmin.AdminUser)
		ckPassword         bool
		newPasswordEncrypt string
	)
	if err = ctx.ReadJSON(&params); err != nil {
		code = define.CodeParamsError
		goto ERR
	}

	if err = storage.GDB1.
		Take(&mUser, "emp_number=? AND cate=?", params.EmpNumber, params.Cate).Error; err != nil {
		if errors.Is(gorm.ErrRecordNotFound, err) {
			code = define.CodeRecordNotFound
		}
		goto ERR
	}

	ckPassword = support.CheckPassword(mUser.Password, params.Password)
	if !ckPassword {
		code = define.CodePasswordError
		goto ERR
	}

	if newPasswordEncrypt, err = support.EncryptPassword(params.NewPassword); err != nil {
		goto ERR
	}
	if err = storage.GDB1.Model(modeladmin.AdminUser{}).
		Where("emp_number=? AND cate=?", params.EmpNumber, params.Cate).Updates(
		map[string]interface{}{
			"password":     newPasswordEncrypt,
			"updated_time": tools.CurrentTimeString(),
		}).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			code = define.CodeRecordNotFound
		}
		goto ERR
	}

	support.Ok_(ctx)
	return
ERR:
	golog.Errorf("原因：%s，错误：%s", code.String(), err)
	support.InternalServerError(ctx, code)
}

// -------------------
func (impl *adminUserService) Create(ctx iris.Context) {
	var (
		code       define.Code
		err        error
		enPassword string
		in         sin.AdminUserIn
	)
	if err = ctx.ReadJSON(&in); err != nil {
		goto ERR
	}
	if enPassword, err = support.EncryptPassword(in.Password); err != nil {
		goto ERR
	}
	in.Password = enPassword
	if err = storage.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Create(&in).Error; err != nil {
			return err
		}
		if len(in.Roles) > 0 {
			casbins := make([]*modeladmin.CasbinRule, 0)
			for _, v := range in.Roles {
				casbins = append(casbins, &modeladmin.CasbinRule{
					Ptype: "g",
					V0:    strconv.Itoa(int(in.Id)),
					V1:    strconv.Itoa(v),
					V2:    define.GConfig.App.Own.Tenant,
				})
			}
			if err = tx.CreateInBatches(casbins, 100).Error; err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		goto ERR
	}
	if err = icasbin.GCasbin.LoadPolicy(); err != nil {
		goto ERR
	}
	support.Ok_(ctx)
	return
ERR:
	golog.Errorf("错误：%s", err)
	support.InternalServerError(ctx, code)
}

func (impl *adminUserService) Delete(ctx iris.Context) {
	var (
		code   define.Code
		err    error
		idList = make([]int, 0)
	)
	if err = ctx.ReadJSON(&idList); err != nil {
		goto ERR
	}
	for _, v := range idList {
		if v == 1 {
			code = define.CodeUserIsRoot
			support.Error_(ctx, iris.StatusBadRequest, code, "超级用户不能删除", nil)
			return
		}
	}

	if err = storage.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Delete(&modeladmin.AdminUser{}, "id in ?", idList).Error; err != nil {
			return err
		}
		if err = tx.Delete(&modeladmin.CasbinRule{}, "ptype='g' AND v0 in ?", idList).Error; err != nil {
			return err
		}
		return nil
	}); err != nil {
		goto ERR
	}
	support.Ok_(ctx)
	return
ERR:
	golog.Errorf("错误：%s", err)
	support.InternalServerError(ctx, code)
}

func (impl *adminUserService) Update(ctx iris.Context) {
	var (
		code define.Code
		err  error
		in   sin.AdminUserIn
	)
	if err = ctx.ReadJSON(&in); err != nil {
		goto ERR
	}
	if err = storage.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Omit("password").Save(&in).Error; err != nil {
			return err
		}
		if err = tx.Delete(&modeladmin.CasbinRule{},
			"ptype='g' AND v0=? AND v2=?", in.Id, define.GConfig.App.Own.Tenant).Error; err != nil {
			return err
		}
		if len(in.Roles) > 0 {
			casbins := make([]*modeladmin.CasbinRule, 0)
			for _, v := range in.Roles {
				casbins = append(casbins, &modeladmin.CasbinRule{
					Ptype: "g",
					V0:    strconv.Itoa(int(in.Id)),
					V1:    strconv.Itoa(v),
					V2:    define.GConfig.App.Own.Tenant,
				})
			}
			if err = tx.CreateInBatches(casbins, 100).Error; err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		goto ERR
	}
	if err = icasbin.GCasbin.LoadPolicy(); err != nil {
		goto ERR
	}
	support.OkTip(ctx, nil)
	return
ERR:
	golog.Errorf("错误：%s", err)
	support.InternalServerError(ctx, code)
}

func (impl *adminUserService) Table(ctx iris.Context) {
	var (
		code   define.Code
		err    error
		in     sin.VxeTableIn
		total  int64
		result = make([]*sout.AdminUserOut, 0)
		db     = storage.GDB1
	)
	if err = ctx.ReadQuery(&in); err != nil {
		code = define.CodeParamsError
		goto ERR
	}
	if err = db.Model(&modeladmin.AdminUser{}).Count(&total).Error; err != nil {
		goto ERR
	}
	if err = db.Omit("password").Find(&result).Error; err != nil {
		goto ERR
	}
	for _, v := range result {
		var roles = make([]*modeladmin.AdminRole, 0)
		if err = storage.GDB1.Raw(`
		select * from admin_role where id in (
		select v1 from casbin_rule where ptype = 'g' and v0=?
		)`, v.Id).Scan(&roles).Error; err != nil {
			goto ERR
		}
		v.Roles = roles
	}
	support.VxeTableData(ctx, in.PageSize, in.CurrentPage, total, result, nil)
	return
ERR:
	golog.Errorf("错误：%s", err)
	support.InternalServerError(ctx, code)
}

func (impl *adminUserService) List(ctx iris.Context) {
	var (
		code   define.Code
		err    error
		in     sin.VxeTableIn
		result = make([]*sout.AdminUserOut, 0)
	)
	if err = ctx.ReadQuery(&in); err != nil {
		goto ERR
	}
	if err = repository.NewCurd().OmitDefault().List(&result); err != nil {
		goto ERR
	}
	support.Ok(ctx, result)
ERR:
	golog.Errorf("错误：%s", err)
	support.InternalServerError(ctx, code)
}
